<?php
namespace Easy\Db\Driver;
use Easy\Db\DbDriver;
/**
 * PDO数据库驱动
 */
class Mssql extends DbDriver{
	
	protected $selectSql  =     'SELECT T1.* FROM (SELECT easyphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS easyphp) AS T1 %LIMIT%%COMMENT%';
	
	//更新表达式
	protected $updateSql  =	 'UPDATE %TABLE% %SET% %WHERE%  %COMMENT%';
	
	//删除表达式
	protected $deleteSql  =	 'DELETE FROM %TABLE% %WHERE% %COMMENT%';
	
	/**
	 * 架构函数 读取数据库配置信息
	 * @access public
	 * @param array $config 数据库配置数组
	 */
	public function __construct($config=''){
		if ( !function_exists('mssql_connect') ) {
			E('_NOT_SUPPERT_:mssql');
		}
		if(!empty($config)) {
			$this->config   =   $config;
			if(empty($this->config['params'])) {
				$this->config['params'] =   array();
			}
		}
	}
	
	/**
	 * 连接数据库方法
	 * @access public
	 */
	protected function connect($config='',$linkNum=0) {
		if ( !isset($this->linkID[$linkNum]) ){
			$pconnect   = !empty($config['params']['persist'])? $config['params']['persist']:$this->pconnect;
			$conn = $pconnect ? 'mssql_pconnect':'mssql_connect';
			// 处理不带端口号的socket连接情况
			$sepr = IS_WIN ? ',' : ':';
			$host = $config['hostname'].($config['hostport']?$sepr."{$config['hostport']}":'');	
			$this->linkID[$linkNum] = $conn( $host, $config['username'], $config['password']);
			if ( !$this->linkID[$linkNum] )  E("Couldn't connect to SQL Server on $host");
			if ( !empty($config['database'])  && !mssql_select_db($config['database'], $this->linkID[$linkNum]) ) {
				E("Couldn't open database '".$config['database']);
			}
			// 标记连接成功
			$this->connected =  true;
		}
		return $this->linkID[$linkNum];
	}
	
	
	/**
	 * 连接分布式服务器
	 * @access protected
	 * @param boolean $master 主服务器
	 * @return void
	 */
	protected function multiConnect($master=false) {
		if($master){
			$host = $this->config['masterhost'];
		}else{
			$host = $this->config['slavehost'];
		}
		$db_config = array(
				'username'  =>  $this->config['username'],
				'password'  =>  $this->config['password'],
				'hostname'  =>  $master?$this->config['masterhost']:$this->config['slavehost'],
				'hostport'  =>  $this->config['hostport'],
				'database'  =>  $this->config['database'],
				'params'    =>  isset($this->config['params'])?$this->config['params']:array(),
				'charset'   =>  $this->config['charset']
	
		);
		return $this->connect($db_config);
	}
	
	/**
	 * 执行查询  返回数据集
	 * @access public
	 * @param string $str  sql指令
	 * @return mixed
	 */
	public function query($sql,$bind=array()) {
		if ( !$this->_linkID ) return false;
		
		foreach ($bind as $cloumn => $val){
			$sql = str_replace($cloumn,"'".$val."'", $sql);
		}
		
		$this->queryStr = $sql;
		//释放前次的查询结果
		if ( $this->queryID ) $this->free();
		$this->queryID = mssql_query($sql, $this->_linkID);
		if (false === $this->queryID ) {
			E($this->error());
			return false;
		} else {
			return $this->queryID;
		}
	}
	
	/**
	 * 执行语句
	 * @access public
	 * @param string $str  sql指令
	 * @return integer
	 */
	public function execute($str) {
		if ( !$this->_linkID ) return false;
		$this->queryStr = $str;
		//释放前次的查询结果
		if ( $this->queryID ) $this->free();
		
		$result	=	mssql_query($str, $this->_linkID);
		if ( false === $result ) {
			E($this->error());
			return false;
		}else {
			$this->numRows = mssql_rows_affected($this->_linkID);
			return $this->numRows;
		}
	}
	
	
	public function insert($table, array $bind){
		$this->_linkID=$this->multiConnect(true);
		// extract and quote col names from the array keys
		$cols = array();
		$vals = array();

		foreach ($bind as $col => $val) {
			$column = $col;
			$cols[] = $column;
			$vals[] = "'".$val."'";
		}
		// build the statement
		$sql = "INSERT INTO $table"
		.' (' . implode(', ', $cols) . ') '
				. 'VALUES (' . implode(', ', $vals) . ')';
		// execute the statement and return the number of affected rows
		$result = $this->execute($sql);
		if($result !== false){
			$lastInserId = $this->mssql_insert_id();
			if($lastInserId){
				$result = $lastInserId;
			}
		}
		return $result;
	}
	
	public function update($sql) {
		$this->_linkID=$this->multiConnect(true);
		/**
		 * Execute the statement and return the number of affected rows
		*/
		foreach ($this->bind as $cloumn => $val){
			$sql = str_replace($cloumn,"'".$val."'", $sql);
		}
		$result = $this->execute($sql);
		return $result;
	}
	
	public function delete($sql){
		$this->_linkID=$this->multiConnect(true);
		foreach ($this->bind as $cloumn => $val){
			$sql = str_replace($cloumn,"'".$val."'", $sql);
		}
		$result = $this->execute($sql);
		return $result;
	}
	
	
	public function fetchAll($sql, $bind = array()){
		$this->_linkID=$this->multiConnect(false);
		$bind = array_merge($bind,$this->bind);
		$queryId = $this->query($sql,$bind);
		if($queryId === false){
			return false;
		}
		$data = array();
		while ($row = mssql_fetch_assoc($queryId)) {
			$data[] = $row;
		}
		return $data;
	}
	
	public function fetchRow($sql, $bind = array()){
		$this->_linkID=$this->multiConnect(false);
		$bind = array_merge($bind,$this->bind);
		$queryId = $this->query($sql,$bind);
		if($queryId === false){
			return false;
		}
		$data = mssql_fetch_assoc($queryId);
		return $data;
	}
	
	public function fetchCol($sql, $bind = array()){
		$this->_linkID=$this->multiConnect(false);
		$bind = array_merge($bind,$this->bind);
		$queryId = $this->query($sql,$bind);
		if($queryId === false){
			return false;
		}
		$field = mssql_fetch_field($queryId,0);
		return $field->name;
	}
	
	public function fetchOne($sql, $bind = array()) {
		$this->_linkID=$this->multiConnect(false);
		$bind = array_merge($bind,$this->bind);
		$queryId = $this->query($sql,$bind);
		if($queryId === false){
			return false;
		}
		$data = mssql_fetch_array($queryId);
		return $data[0];
	}
	
	public function fetchPairs($sql, $bind = array()){
		$this->_linkID=$this->multiConnect(false);
		$bind = array_merge($bind,$this->bind);
		$queryId = $this->query($sql,$bind);
		if($queryId === false){
			return false;
		}
		$data = array();
		while ($row = mssql_fetch_array($queryId,2)) {
			$data[$row[0]] = $row[1];
		}
		return $data;
	}
		
	/**
	 * 释放查询结果
	 * @access public
	 */
	public function free() {
		mssql_free_result($this->queryID);
		$this->queryID = null;
	}
	
	/**
	 * 用于获取最后插入的ID
	 * @access public
	 * @return integer
	 */
	public function mssql_insert_id() {
		$query  =   "SELECT @@IDENTITY as last_insert_id";
		$result =   mssql_query($query, $this->_linkID);
		list($last_insert_id)   =   mssql_fetch_row($result);
		mssql_free_result($result);
		return $last_insert_id;
	}
	
	/**
	 * 启动事务
	 * @access public
	 * @return void
	 */
	public function startTrans() {
		$this->initConnect(true);
		if ( !$this->_linkID ) return false;
		//数据rollback 支持
		if ($this->transTimes == 0) {
			mssql_query('BEGIN TRAN', $this->_linkID);
		}
		$this->transTimes++;
		return ;
	}
	
	/**
	 * 用于非自动提交状态下面的查询提交
	 * @access public
	 * @return boolen
	 */
	public function commit() {
		if ($this->transTimes > 0) {
			$result = mssql_query('COMMIT TRAN', $this->_linkID);
			$this->transTimes = 0;
			if(!$result){
				$this->error();
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 事务回滚
	 * @access public
	 * @return boolen
	 */
	public function rollback() {
		if ($this->transTimes > 0) {
			$result = mssql_query('ROLLBACK TRAN', $this->_linkID);
			$this->transTimes = 0;
			if(!$result){
				$this->error();
				return false;
			}
		}
		return true;
	}
	
	
	/**
	 * order分析
	 * @access protected
	 * @param mixed $order
	 * @return string
	 */
	protected function parseOrder($order) {
		return !empty($order)?  ' ORDER BY '.$order:' ORDER BY rand()';
	}
	
	/**
	 * limit
	 * @access public
	 * @return string
	 */
	public function parseLimit($limit) {
		if(empty($limit)) return '';
		$limit	=	explode(',',$limit);
		if(count($limit)>1)
			$limitStr	=	'(T1.ROW_NUMBER BETWEEN '.$limit[0].' + 1 AND '.$limit[0].' + '.$limit[1].')';
		else
			$limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND '.$limit[0].")";
		return ' WHERE '.$limitStr;
	}
	
	
	
	/**
	 * 关闭数据库
	 * @access public
	 */
	public function close() {
		if ($this->_linkID){
			mssql_close($this->_linkID);
		}
		$this->_linkID = null;
	}
	
	/**
	 * 数据库错误信息
	 * 并显示当前的SQL语句
	 * @access public
	 * @return string
	 */
	public function error() {
		$this->error = mssql_get_last_message();
		if('' != $this->queryStr){
			$this->error .= "\n [ SQL语句 ] : ".$this->queryStr;
		}
		return $this->error;
	}
}